/*
 * Copyright (c) 2010, Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

/**
 * \file
 *         Example of how the collect primitive works.
 * \author
 *         Adam Dunkels <adam@sics.se>
 */

#include "contiki.h"

#include "net/ip/uip.h"
#include "net/ipv6/uip-ds6.h"
#include "net/ip/uip-udp-packet.h"
#include "net/rpl/rpl.h"

#include "lib/random.h"
#include "net/netstack.h"
#include "dev/leds.h"
#include "collect-common.h"

#define DEBUG DEBUG_PRINT
#include "net/ip/uip-debug.h"
#include "ffz/ffzconfig.h"

#include <stdio.h>
#include <string.h>
#include <ctype.h>

static unsigned long time_offset;
static int send_active = 1;
static struct uip_udp_conn *client_conn = NULL;
static uip_ipaddr_t *server_ipaddr = NULL;

#ifndef PERIOD
#define PERIOD 60
#endif
#define RANDWAIT (PERIOD)

/*---------------------------------------------------------------------------*/
PROCESS(collect_common_process, "collect common process");
// AUTOSTART_PROCESSES(&collect_common_process);
/*---------------------------------------------------------------------------*/
static unsigned long
get_time(void)
{
  return clock_seconds() + time_offset;
}
/*---------------------------------------------------------------------------*/
void collect_common_set_send_active(int active)
{
  send_active = active;
}
/*---------------------------------------------------------------------------*/
void collect_common_net_init(struct uip_udp_conn *conn, uip_ipaddr_t *ipaddr)
{
  client_conn = conn;
  server_ipaddr = ipaddr;
}
/*---------------------------------------------------------------------------*/
void collect_common_set_sink(void)
{
  /* A udp client can never become sink */
}
/*---------------------------------------------------------------------------*/
void collect_common_net_print(void)
{
#ifndef UDP_SERVER
  // rpl_dag_t *dag;
  // uip_ds6_route_t *r;

  // /* Let's suppose we have only one instance */
  // dag = rpl_get_any_dag();
  // if (dag->preferred_parent != NULL)
  // {
  //   PRINTF("Preferred parent: ");
  //   PRINT6ADDR(rpl_get_parent_ipaddr(dag->preferred_parent));
  //   PRINTF("\n");
  // }
  // for (r = uip_ds6_route_head();
  //      r != NULL;
  //      r = uip_ds6_route_next(r))
  // {
  //   PRINT6ADDR(&r->ipaddr);
  // }
  // PRINTF("---\n");
#endif
}
/*---------------------------------------------------------------------------*/
void collect_view_construct_message(struct collect_view_data_msg *msg,
                                    const linkaddr_t *parent,
                                    uint16_t parent_etx,
                                    uint16_t current_rtmetric,
                                    uint16_t num_neighbors,
                                    uint16_t beacon_interval)
{
  static unsigned long last_cpu, last_lpm, last_transmit, last_listen;
  unsigned long cpu, lpm, transmit, listen;

  msg->len = sizeof(struct collect_view_data_msg) / sizeof(uint16_t);
  msg->clock = clock_time();
#if TIMESYNCH_CONF_ENABLED
  msg->timesynch_time = timesynch_time();
#else  /* TIMESYNCH_CONF_ENABLED */
  msg->timesynch_time = 0;
#endif /* TIMESYNCH_CONF_ENABLED */

  energest_flush();

  cpu = energest_type_time(ENERGEST_TYPE_CPU) - last_cpu;
  lpm = energest_type_time(ENERGEST_TYPE_LPM) - last_lpm;
  transmit = energest_type_time(ENERGEST_TYPE_TRANSMIT) - last_transmit;
  listen = energest_type_time(ENERGEST_TYPE_LISTEN) - last_listen;

  /* Make sure that the values are within 16 bits. If they are larger,
     we scale them down to fit into 16 bits. */
  while (cpu >= 65536ul || lpm >= 65536ul ||
         transmit >= 65536ul || listen >= 65536ul)
  {
    cpu /= 2;
    lpm /= 2;
    transmit /= 2;
    listen /= 2;
  }

  msg->cpu = cpu;
  msg->lpm = lpm;
  msg->transmit = transmit;
  msg->listen = listen;

  last_cpu = energest_type_time(ENERGEST_TYPE_CPU);
  last_lpm = energest_type_time(ENERGEST_TYPE_LPM);
  last_transmit = energest_type_time(ENERGEST_TYPE_TRANSMIT);
  last_listen = energest_type_time(ENERGEST_TYPE_LISTEN);

  memcpy(&msg->parent, &parent->u8[LINKADDR_SIZE - 2], 2);
  msg->parent_etx = parent_etx;
  msg->current_rtmetric = current_rtmetric;
  msg->num_neighbors = num_neighbors;
  msg->beacon_interval = beacon_interval;

  // memset(msg->sensors, 0, sizeof(msg->sensors));
  // collect_view_arch_read_sensors(msg);
}
/*---------------------------------------------------------------------------*/
void collect_common_send(void)
{
#ifndef UDP_SERVER
  static uint8_t seqno;
  struct
  {
    uint8_t seqno;
    uint8_t for_alignment;
    struct collect_view_data_msg msg;
  } msg;
  /* struct collect_neighbor *n; */
  uint16_t parent_etx;
  uint16_t rtmetric;
  uint16_t num_neighbors;
  uint16_t beacon_interval;
  rpl_parent_t *preferred_parent;
  linkaddr_t parent;
  rpl_dag_t *dag;

  if (client_conn == NULL)
  {
    /* Not setup yet */
    return;
  }
  // printf("sizeof(msg) = %d\n", sizeof(msg));   46
  memset(&msg, 0, sizeof(msg));
  seqno++;
  if (seqno == 0)
  {
    /* Wrap to 128 to identify restarts */
    seqno = 128;
  }
  msg.seqno = seqno;

  linkaddr_copy(&parent, &linkaddr_null);
  parent_etx = 0;

  /* Let's suppose we have only one instance */
  dag = rpl_get_any_dag();
  if (dag != NULL)
  {
    preferred_parent = dag->preferred_parent;
    if (preferred_parent != NULL)
    {
      uip_ds6_nbr_t *nbr;
      nbr = uip_ds6_nbr_lookup(rpl_get_parent_ipaddr(preferred_parent));
      if (nbr != NULL)
      {
        /* Use parts of the IPv6 address as the parent address, in reversed byte order. */
        parent.u8[LINKADDR_SIZE - 1] = nbr->ipaddr.u8[sizeof(uip_ipaddr_t) - 2];
        parent.u8[LINKADDR_SIZE - 2] = nbr->ipaddr.u8[sizeof(uip_ipaddr_t) - 1];
        parent_etx = rpl_get_parent_rank((uip_lladdr_t *)uip_ds6_nbr_get_ll(nbr)) / 2;
      }
    }
    rtmetric = dag->rank;
    beacon_interval = (uint16_t)((2L << dag->instance->dio_intcurrent) / 1000);
    num_neighbors = uip_ds6_nbr_num();
  }
  else
  {
    rtmetric = 0;
    beacon_interval = 0;
    num_neighbors = 0;
  }

  /* num_neighbors = collect_neighbor_list_num(&tc.neighbor_list); */
  collect_view_construct_message(&msg.msg, &parent,
                                 parent_etx, rtmetric,
                                 num_neighbors, beacon_interval);

  PRINTF("DEBUG collect-common.c send\n");
  uip_udp_packet_sendto(client_conn, &msg, sizeof(msg),
                        server_ipaddr, UIP_HTONS(UDP_CLIENT_PORT));
#endif
}
/*---------------------------------------------------------------------------*/
void collect_common_recv(const linkaddr_t *originator, uint8_t seqno, uint8_t hops,
                         uint8_t *payload, uint16_t payload_len)
{
  unsigned long time;
  uint16_t data;
  int i;

  // if (payload_len != 44)
  // {
  //   return;
  // }

  printf("%u", 8 + payload_len / 2);
  /* Timestamp. Ignore time synch for now. */
  time = get_time();
  printf(" %lu %lu 0", ((time >> 16) & 0xffff), time & 0xffff);
  /* Ignore latency for now */
  printf(" %u %u %u %u",
         originator->u8[0] + (originator->u8[1] << 8), seqno, hops, 0);
  for (i = 0; i < payload_len / 2; i++)
  {
    memcpy(&data, payload, sizeof(data));
    payload += sizeof(data);
    printf(" %u", data);
  }
  printf("\n");
  leds_blink();
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(collect_common_process, ev, data)
{
  static struct etimer period_timer;
  PROCESS_BEGIN();

  PRINTF("Collect Process Started.............\n");

  /* Send a packet every 60-62 seconds. */
  etimer_set(&period_timer, CLOCK_SECOND * PERIOD);
  while (1)
  {
    PROCESS_WAIT_EVENT();
    if (client_conn == NULL)
    {
      continue;
    }
    if (ev == PROCESS_EVENT_TIMER && data == &period_timer)
    {
      etimer_reset(&period_timer);
      if (send_active)
      {
        /* Time to send the data */
        PRINTF("DEBUG send_active %u \n", send_active);
        collect_common_send();
      }
    }
  }

  PROCESS_END();
}
/*---------------------------------------------------------------------------*/
